package com.example.todolist.service.impl;

import com.example.todolist.model.dto.TodoItemDto;
import com.example.todolist.model.entity.TodoItem;
import com.example.todolist.repository.TodoRepository;
import com.example.todolist.service.TodoService;
import com.example.todolist.mq.TodoEvent;
import com.example.todolist.service.TodoEventPublisher;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional
public class TodoServiceImpl implements TodoService {

    private final TodoRepository todoRepository;
    private final ModelMapper modelMapper;
    private final TodoEventPublisher eventPublisher;

    @Override
    @Cacheable(value = "todos", key = "'all'")
    public List<TodoItemDto> getAllTodoItems() {
        return getTodoItemsFromDb();
    }

    @Override
    @Cacheable(value = "todos", key = "'completed'")
    public List<TodoItemDto> getCompletedItems() {
        return getCompletedItemsFromDb();
    }

    @Override
    @Cacheable(value = "todos", key = "'incomplete'")
    public List<TodoItemDto> getIncompleteItems() {
        return getIncompleteItemsFromDb();
    }

    @Override
    @Cacheable(value = "todo", key = "#id")
    public TodoItemDto getTodoItemById(Long id) {
        TodoItem todoItem = todoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("TodoItem not found with id: " + id));
        return convertToDto(todoItem);
    }

    @Override
    @Caching(
            put = {@CachePut(value = "todo", key = "#result.id")},
            evict = {@CacheEvict(value = "todos", allEntries = true)}
    )
    public TodoItemDto createTodoItem(TodoItemDto todoItemDto) {
        TodoItem todoItem = convertToEntity(todoItemDto);
        TodoItem savedItem = todoRepository.save(todoItem);
        TodoItemDto result = convertToDto(savedItem);

        // 发布创建事件
        eventPublisher.publishTodoEvent("CREATED", result);
        return result;
    }

    @Override
    @Caching(
            put = {@CachePut(value = "todo", key = "#id")},
            evict = {@CacheEvict(value = "todos", allEntries = true)}
    )
    public TodoItemDto updateTodoItem(Long id, TodoItemDto todoItemDto) {
        TodoItem existingItem = todoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("TodoItem not found with id: " + id));

        existingItem.setTitle(todoItemDto.getTitle());
        existingItem.setDescription(todoItemDto.getDescription());
        existingItem.setCompleted(todoItemDto.isCompleted());

        TodoItem updatedItem = todoRepository.save(existingItem);
        TodoItemDto result = convertToDto(updatedItem);

        // 发布更新事件
        eventPublisher.publishTodoEvent("UPDATED", result);
        return result;
    }

    @Override
    @Caching(
            evict = {
                    @CacheEvict(value = "todo", key = "#id"),
                    @CacheEvict(value = "todos", allEntries = true)
            }
    )
    public void deleteTodoItem(Long id) {
        // 先获取待删除的条目用于通知
        TodoItem todoItem = todoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("TodoItem not found with id: " + id));

        todoRepository.deleteById(id);

        // 发布删除事件
        eventPublisher.publishTodoEvent("DELETED", convertToDto(todoItem));
    }

    @Override
    @Caching(
            put = {@CachePut(value = "todo", key = "#id")},
            evict = {@CacheEvict(value = "todos", allEntries = true)}
    )
    public TodoItemDto toggleTodoItemCompletion(Long id) {
        TodoItem todoItem = todoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("TodoItem not found with id: " + id));

        todoItem.setCompleted(!todoItem.isCompleted());
        TodoItem updatedItem = todoRepository.save(todoItem);
        TodoItemDto result = convertToDto(updatedItem);

        // 发布更新事件
        eventPublisher.publishTodoEvent("UPDATED", result);
        return result;
    }


    private List<TodoItemDto> getTodoItemsFromDb() {
        return todoRepository.findByOrderByCreatedAtDesc()
                .stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    private List<TodoItemDto> getCompletedItemsFromDb() {
        return todoRepository.findByCompleted(true)
                .stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    private List<TodoItemDto> getIncompleteItemsFromDb() {
        return todoRepository.findByCompleted(false)
                .stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    private TodoItemDto convertToDto(TodoItem todoItem) {
        return modelMapper.map(todoItem, TodoItemDto.class);
    }

    private TodoItem convertToEntity(TodoItemDto todoItemDto) {
        return modelMapper.map(todoItemDto, TodoItem.class);
    }
}